package com.itCar.base.api.wxApi.wxAuth.util;

import com.alibaba.fastjson.JSONObject;
import com.baidu.aip.ocr.AipOcr;
import com.itCar.base.api.wxApi.wxAuth.pojo.*;
import com.itCar.base.api.wxApi.wxAuth.pojo.info.*;
import com.thoughtworks.xstream.XStream;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONArray;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.net.ssl.HttpsURLConnection;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @ClassName: WxAuthUtil
 * @Description: TODO
 * @author: liuzg
 * @Date: 2022/8/31 15:17
 * @Week: 星期三
 * @Version: v1.0
 */
@Slf4j
public class WxAuthUtil {

    // 微信公众号 获取access_token
    private static final String APPID = "wx148e4e32f56a200e";
    private static final String APPSECRET = "4a1ebdbfaec3aee2cf1f6931b4630662";
    private static final String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";

    //百度ai 设置APPID/AK/SK
    public static final String APP_ID = "27252904";
    public static final String API_KEY = "QNaK4cvYC6tPFVbXFfNxNN7A";
    public static final String SECRET_KEY = "N7erFcTkV8hHMFHBeWTStKvNs7IL8hU9";

    private static AccessToken at;

    /**
     * 向外暴露的获取access_token的方法
     *
     * @return
     */
    public static String getAccessToken() {
        if (at == null || at.isExpires()) {
            getToken();
        }
        System.out.println(at.getAccessToken());
        return at.getAccessToken();
    }

    /**
     * 获取access_token
     */
    private static void getToken() {
        String url = GET_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
        String access_token = Utils.get(url);
        JSONObject accToken = JSONObject.parseObject(access_token);
        String token = accToken.getString("access_token");
        String expiresIn = accToken.getString("expires_in");
        at = new AccessToken(token, expiresIn);
    }

    /**
     * 验证签名
     *
     * @param timestamp 时间戳
     * @param nonce     随机数
     * @param token
     * @param signature 微信加密签名，signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数
     * @return
     */
    public static boolean check(String timestamp, String nonce, String token, String signature) {
        // 1）将token、timestamp、nonce三个参数进行字典序排序
        String[] strs = new String[]{token, timestamp, nonce};
        Arrays.sort(strs);
        // 2）将三个参数字符串拼接成一个字符串进行sha1加密
        String str = strs[0] + strs[1] + strs[2];
        String mysig = sha1(str);
        // 3）开发者获得加密后的字符串可与 signature 对比，标识该请求来源于微信
        return mysig.equalsIgnoreCase(signature);
    }

    /**
     * 进行sha1加密
     *
     * @param str
     * @return
     */
    private static String sha1(String str) {
        // 获取一个加密对象
        try {
            MessageDigest md = MessageDigest.getInstance("sha1");
            // 加密
            byte[] digest = md.digest(str.getBytes());
            char[] chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
            StringBuilder builder = new StringBuilder();
            // 处理加密结果
            for (byte b : digest) {
                builder.append(chars[(b >> 4) & 15]);
                builder.append(chars[b & 15]);
            }
            return builder.toString();
        } catch (NoSuchAlgorithmException e) {
            log.error("获取sha1加密对象失败");
        }
        return null;
    }

    /**
     * 解析Xml数据包
     *
     * @param is
     * @return
     */
    public static Map<String, String> parseRequest(InputStream is) {
        Map<String, String> map = new HashMap<>();
        SAXReader reader = new SAXReader();
        try {
            // 读取输入流， 获取文档对象
            Document document = reader.read(is);
            // 根据文档对象获取根节点
            Element root = document.getRootElement();
            // 获取根节点下的所有子节点
            List<Element> elements = root.elements();
            for (Element element : elements) {
                map.put(element.getName(), element.getStringValue());
            }
        } catch (DocumentException e) {
            log.error(e.getMessage());
        }
        return map;
    }

    /**
     * 用于处理所有的事件和消息回复
     *
     * @param requestMap
     * @return 返回的是xml数据包
     */
    public static String getResponse(Map<String, String> requestMap) {
        BaseMessage msg = null;
        String msgType = requestMap.get("MsgType");
        switch (msgType) {
            case "text": // 文本
                msg = dealTextMessage(requestMap);
                break;
            case "image": // 图片
                msg = dealImage(requestMap);
                break;
            case "voice": // 语音
                break;
            case "video": // 视频
                break;
            case "music": // 音乐
                break;
            case "news": // 图文
                break;
            case "event": // 自定义菜单响应
                msg = dealEvent(requestMap);
                break;
            default:
                break;
        }

        // 把消息对象处理为xml数据包
        if (msg != null) {
            return beanToXml(msg);
        }
        return null;
    }

    /**
     * 进行图片识别
     *
     * @param requestMap
     * @return
     */
    private static BaseMessage dealImage(Map<String, String> requestMap) {
        // 初始化一个AipOcr
        AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
        // 可选：设置网络连接参数
        client.setConnectionTimeoutInMillis(2000);
        client.setSocketTimeoutInMillis(60000);

        // 调用接口 图片地址直接写传入的requestMap中的
        String path = requestMap.get("PicUrl");
        // 本地图片调用
//        org.json.JSONObject basicGeneral = client.basicGeneral(path, new HashMap<>());
        // 直接通过网络图片调用
        org.json.JSONObject basicGeneral = client.generalUrl(path, new HashMap<String, String>());
        String json = basicGeneral.toString();
        net.sf.json.JSONObject jsonObject = net.sf.json.JSONObject.fromObject(json);
        JSONArray wordsResult = jsonObject.getJSONArray("words_result");
        Iterator<JSONObject> iterator = wordsResult.iterator();
        StringBuilder sb = new StringBuilder();
        while (iterator.hasNext()) {
            JSONObject next = iterator.next();
            sb.append(next.getString("words"));
        }

        return new TextMessage(requestMap, sb.toString());
    }


    /**
     * 处理事件推送
     *
     * @param requestMap
     * @return
     */
    private static BaseMessage dealEvent(Map<String, String> requestMap) {
        String event = requestMap.get("Event");
        switch (event) {
            case "CLICK":
                return dealClick(requestMap);
            case "VIEW":
                return dealView(requestMap);
            default:
                break;
        }


        return null;
    }

    /**
     * 专门处理view类型的按钮菜单
     *
     * @param requestMap
     * @return
     */
    private static BaseMessage dealView(Map<String, String> requestMap) {

        return null;
    }

    /**
     * 专门处理click类型的按钮菜单
     *
     * @param requestMap
     * @return
     */
    private static BaseMessage dealClick(Map<String, String> requestMap) {
        String eventKey = requestMap.get("EventKey");
        switch (eventKey) {
            case "1": // 点击一级菜单
                return new TextMessage(requestMap, "你点击了一下第一个一级菜单");
            case "32": // 点击第三个一级菜单的第二个子菜单

                break;
            default:
                break;
        }
        return null;
    }

    /**
     * 把消息对象处理为xml数据包
     *
     * @param msg
     * @return
     */
    private static String beanToXml(BaseMessage msg) {
        XStream stream = new XStream();
        stream.processAnnotations(TextMessage.class);
        stream.processAnnotations(ImageMessage.class);
        stream.processAnnotations(MusicMessage.class);
        stream.processAnnotations(NewsMessage.class);
        stream.processAnnotations(VideoMessage.class);
        stream.processAnnotations(VoiceMessage.class);
        String xml = stream.toXML(msg);
        return xml;
    }

    /**
     * 处理文本消息
     * 网易七鱼 域名：https://csdl2.qiyukf.com/（现在无用）
     * 调用的是图灵接口 每日应该10次，谨慎调试
     *
     * @param requestMap
     * @return
     */
    private static BaseMessage dealTextMessage(Map<String, String> requestMap) {
        // 获取用户发送的内容
        String msg = requestMap.get("Content");
        if (msg.equals("图文")) {
            List<Article> articles = new ArrayList<>();
            articles.add(new Article("这是图文消息的标题", "这是图文消息的详细介绍", "发送的图片地址", "www.baidu.com"));
            NewsMessage news = new NewsMessage(requestMap, articles);
            return news;
        }
        // 调用方法赶回聊天内容
        String resp = chat(msg);
        TextMessage tm = new TextMessage(requestMap, resp);
        return tm;
    }

    /**
     * 调用图灵机器人聊天
     * apikey: 1702c05fc1b94e2bb4de7fb2e61b21a3
     *
     * @param msg 发送的消息
     * @return
     */
    private static String chat(String msg) {
        Utils util = new Utils();
        String message = util.getMessage(msg);
        return message;
    }


    /**
     * 上传临时素材
     *
     * @param path 上传文件路径
     * @param type 类型
     * @return
     */
    public static String uploadMaterial(String path, String type) {
        File file = new File(path);
        String url = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=" + getAccessToken() + "&type=" + type;
        try {
            URL u = new URL(url);
            // 强转为安全连接
            HttpsURLConnection conn = (HttpsURLConnection) u.openConnection();
            // 设置连接信息
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setUseCaches(false);
            // 设置请求头信息
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Charset", "utf8");
            // 数据边界
            String boundary = "---" + System.currentTimeMillis();
            conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

            // 获取输出流
            OutputStream out = conn.getOutputStream();
            // 创建文件输入流
            InputStream is = new FileInputStream(file);

            // 第一部分：头部信息
            StringBuilder sb = new StringBuilder();
            sb.append("--");
            sb.append(boundary);
            sb.append("\r\n");
            sb.append("Content-Disposition:form-data;name=\"media\";filename=\"" + file.getName() + "\"\r\n");
            sb.append("Content-Type:application/octet-stream\r\n\r\n");
            System.out.println(sb.toString());
            out.write(sb.toString().getBytes());
            out.flush();
            out.close();
            // 第二部分：文件内容
            byte[] bytes = new byte[1024];
            int len;
            while ((len = is.read(bytes)) != -1) {
                out.write(bytes, 0, len);
            }
            // 第三部分：尾部信息
            String foot = "\r\n--" + boundary + "--\r\n";
            out.write(foot.getBytes());
            out.flush();
            out.close();

            // 读取数据
            InputStream is2 = conn.getInputStream();
            StringBuilder resp = new StringBuilder();
            while ((len = is2.read(bytes)) != -1) {
                resp.append(new String(bytes, 0, len));
            }

            return resp.toString();
        } catch (Exception e) {
            log.error(e.getMessage());
        }


        return null;

    }

    /**
     * 获取带参数二维码的ticket
     */
    public static void getQrScene() {
        String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+ getAccessToken();
        // 生成临时二维码
        String data = "{\n" +
                "    \"expire_seconds\":600,\n" +
                "    \"action_name\":\"QR_STR_SCENE\",\n" +
                "    \"action_info\":{\n" +
                "        \"scene\":{\n" +
                "            \"scene_str\":\"我是你爸爸\"\n" +
                "        }\n" +
                "    }\n" +
                "}";
        String result = Utils.post(url, data);
        String ticket = net.sf.json.JSONObject.fromObject(result).getString("ticket");
        System.out.println(ticket);

    }

}
